import {
  getColsArr,
  getDuplicates,
  hasElementInArrayWithValue,
  getExcelCellContent,
  includeStr,
  endWithStr
} from './utils/index'

class FormatValidation {
  // 构造函数
  constructor(worksheets, fileName) {
    this.fileName = fileName
    this.checkResult = []
    this.buildFileStruct(worksheets)
  }

  // 遍历汇总表，构建文件结构
  buildFileStruct(worksheets) {
    // 定义汇总表内工程名称数组
    let projectArrayInSummaryTable = []
    // 获取汇总表Sheet数组
    const summarySheetArr = worksheets.filter((worksheet) => worksheet.name.includes('汇总'))
    // 汇总表数量
    const summarySheetCount = summarySheetArr.length
    // 多汇总表报错返回
    if (summarySheetCount !== 1) {
      this.checkResult = [
        {
          fileName: this.fileName,
          sheetName: '全局',
          tableType: '全局',
          checkPlace: '全局',
          ruleId: 'FSSJY00036',
          errorInfo:
            '结算表文件中应含有1张汇总表sheet，实际识别数量为[' +
            summarySheetCount +
            ']，请根据模板核对'
        }
      ]
      return
    }
    // 获取汇总sheet
    const summarySheet = summarySheetArr[0]
    // 获取合计行对象
    const cols = getColsArr(summarySheet, 'A-C列')
    const totalRowObj = hasElementInArrayWithValue(['合计', '总计', '总造价'], cols)
    // 无合计行报错返回
    if (!totalRowObj.exist) {
      this.checkResult.push({
        fileName: this.fileName,
        sheetName: summarySheet.name,
        tableType: '汇总表',
        checkPlace: 'A-C列',
        ruleId: 'FSSJY00032',
        errorInfo: '送审表模板错误，未找到“合计”'
      })
      return
    }
    // 获取汇总表表头检测区单元格对象数组{ address, value }
    const tableTitle = getExcelCellContent(summarySheet, 'A3-H5')
    // 检测是否具有序号列
    if (!hasElementInArrayWithValue(['序号', '序'], tableTitle).exist) {
      this.checkResult.push({
        fileName: this.fileName,
        sheetName: summarySheet.name,
        tableType: '汇总表',
        checkPlace: 'A3-H5',
        ruleId: 'FSSJY00001',
        errorInfo: '送审表模板错误，未找到“序号”'
      })
    }
    // 检测是否具有金额列
    if (
      !hasElementInArrayWithValue(
        ['送审金额', '金额', '金额(元)', '金额（元）', '金额(元）', '金额（元)'],
        tableTitle
      ).exist
    ) {
      this.checkResult.push({
        fileName: this.fileName,
        sheetName: summarySheet.name,
        tableType: '汇总表',
        checkPlace: 'A3-H5',
        ruleId: 'FSSJY00003',
        errorInfo: '送审表模板错误，未找到“送审金额”'
      })
    }
    // 获取工程名称列头对象
    const projectNameTitleObj = hasElementInArrayWithValue(
      ['工程名称', '单位工程名称', '项目名称'],
      tableTitle
    )
    // 判断工程名称列是否存在
    if (!projectNameTitleObj.exist) {
      this.checkResult.push({
        fileName: this.fileName,
        sheetName: summarySheet.name,
        tableType: '汇总表',
        checkPlace: 'A3-H5',
        ruleId: 'FSSJY00002',
        errorInfo: '送审表模板错误，未找到“工程名称”'
      })
    } else {
      // 提取列标和行号
      let col = projectNameTitleObj.address
        .substring(0, projectNameTitleObj.address.search(/[0-9]+$/))
        .toUpperCase()
      let row = Number(
        projectNameTitleObj.address.substring(projectNameTitleObj.address.search(/[0-9]+$/))
      )
      // 获取工程名称列
      const colValue = summarySheet.getColumn(col)
      // 计算工程列表起始位置
      if (
        summarySheet.getCell(projectNameTitleObj.address).value ==
        summarySheet.getCell(col + (row + 1)).value
      )
        row = row + 2
      else row = row + 1

      // 获取工程名称数组
      projectArrayInSummaryTable = colValue.values
        .slice(row, Number(totalRowObj.address.substring(totalRowObj.address.search(/[0-9]+$/))))
        .filter((item) => item.length > 0)
      // 判断是否有重复名称,如重复即返回
      const duplicateElementsArr = getDuplicates(projectArrayInSummaryTable)
      if (duplicateElementsArr.length > 0) {
        const duplicateElements = duplicateElementsArr.join(',')
        this.checkResult.push({
          fileName: this.fileName,
          sheetName: summarySheet.name,
          tableType: '汇总表',
          checkPlace: '单位工程名称列',
          ruleId: 'FSSJY00040',
          errorInfo: '送审信息错误，汇总表的单位工程名称[' + duplicateElements + ']重复'
        })
        return
      }
    }
    const subProjectList = this.buildSubItemList(worksheets)
    this.tableMatrix = {
      summarySheet: {
        tableName: summarySheet.name,
        worksheet: summarySheet
      },
      subProjectList
    }
    subProjectList.forEach((value, key) => {
      Object.keys(value).forEach((table) => {
        if (value[table].length != 1) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: '全局',
            tableType: '全局',
            checkPlace: '全局',
            ruleId:
              table == '费率表'
                ? 'FSSJY00037'
                : table == '分部分项表'
                  ? 'FSSJY00038'
                  : 'FSSJY00039',
            errorInfo:
              table == '费率表'
                ? '子单项[' +
                  key +
                  ']应存在1张费率表，实际存在[' +
                  value[table].length +
                  ']张，请检查sheet名称'
                : table == '分部分项表'
                  ? '子单项[' +
                    key +
                    ']应存在1张分部分项表，实际存在[' +
                    value[table].length +
                    ']张，请检查sheet名称'
                  : '子单项[' +
                    key +
                    ']应存在1张技术措施表，实际存在[' +
                    value[table].length +
                    ']张，请检查sheet名称'
          })
        }
      })
    })
  }

  // 构建分项列表
  buildSubItemList(worksheets) {
    const projectList = new Map()
    worksheets.forEach((worksheet) => {
      // 跳过汇总表
      if (worksheet.name.includes('汇总')) return
      // 根据sheet名称判断表类型
      let sheetType
      let projectErrorCode
      if (worksheet.name.includes('费率表')) {
        sheetType = '费率表'
        projectErrorCode = 'FSSJY00042'
      } else if (worksheet.name.includes('分部分项')) {
        sheetType = '分部分项表'
        projectErrorCode = 'FSSJY00034'
      } else if (worksheet.name.includes('技术措施')) {
        sheetType = '技术措施表'
        projectErrorCode = 'FSSJY00031'
      } else {
        return
      }
      // 获得工程名称并判断是否合规
      const projectRanges = getExcelCellContent(worksheet, 'A2-A3')
      const projectNameRange = includeStr(projectRanges, '工程名称')
      if (!projectNameRange.exist) {
        this.checkResult.push({
          fileName: this.fileName,
          sheetName: worksheet.name,
          tableType: sheetType,
          checkPlace: 'A2、A3',
          ruleId: projectErrorCode,
          errorInfo: '送审表模板错误，未找到“工程名称”'
        })
        return
      }
      const regex = /工程名称:(.+)/
      const projectName = worksheet
        .getCell(projectNameRange.address)
        .value.toString()
        .replace(/ /g, '')
        .replace(/\n/g, '')
        .replace(/\r/g, '')
        .replace(/\t/g, '')
        .replace(/[:：]/g, ':')
        .match(regex)[1]
      if (projectName.trim() == '') {
        this.checkResult.push({
          fileName: this.fileName,
          sheetName: worksheet.name,
          tableType: sheetType,
          checkPlace: 'A2、A3',
          ruleId: 'FSSJY00041',
          errorInfo: '工程名称不可为空'
        })
        return
      }
      if (sheetType == '费率表') {
        // 获取费率表表头检测区单元格对象数组{ address, value }
        const tableTitle = getExcelCellContent(worksheet, 'A3-F4')
        // 检测是否具有序号列
        if (!hasElementInArrayWithValue(['序号', '序'], tableTitle).exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-F4',
            ruleId: 'FSSJY00007',
            errorInfo: '送审表模板错误，未找到“序号”'
          })
        }

        // 检测是否具有送审金额列
        if (
          !hasElementInArrayWithValue(
            ['送审金额', '金额', '金额(元)', '金额（元）', '金额(元）', '金额（元)'],
            tableTitle
          ).exist
        ) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-F4',
            ruleId: 'FSSJY00010',
            errorInfo: '送审表模板错误，未找到“送审金额”'
          })
        }
        // 检测是否具有计算列
        if (!includeStr(tableTitle, '计算').exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-F4',
            ruleId: 'FSSJY00009',
            errorInfo: '/'
          })
        }
        // 检测是否具有汇总内容列
        const summaryContent = hasElementInArrayWithValue(
          ['汇总内容', '费用项目名称', '费用项名称', '项目名称', '费用名称'],
          tableTitle
        )
        if (!summaryContent.exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-F4',
            ruleId: 'FSSJY00008',
            errorInfo: '送审表模板错误，未找到唯一“汇总内容”'
          })
        } else {
          // 提取列标
          let col = summaryContent.address
            .substring(0, summaryContent.address.search(/[0-9]+$/))
            .toUpperCase()
          // 获取汇总内容列
          const colValue = worksheet
            .getColumn(col)
            .values.map((v) => (v == null ? '' : v).replace(/ /g, ''))
          // 判断是否包含规费
          if (!colValue.includes('规费')) {
            this.checkResult.push({
              fileName: this.fileName,
              sheetName: worksheet.name,
              tableType: sheetType,
              checkPlace: '汇总内容列',
              ruleId: 'FSSJY00047',
              errorInfo: '费率表信息缺失，未找到“规费”'
            })
          }
          // 判断是否包含税金
          if (!colValue.includes('税金')) {
            this.checkResult.push({
              fileName: this.fileName,
              sheetName: worksheet.name,
              tableType: sheetType,
              checkPlace: '汇总内容列',
              ruleId: 'FSSJY00048',
              errorInfo: '费率表信息缺失，未找到“税金”'
            })
          }
          // 判断是否包含措施项目费
          if (!colValue.includes('措施项目费')) {
            this.checkResult.push({
              fileName: this.fileName,
              sheetName: worksheet.name,
              tableType: sheetType,
              checkPlace: '汇总内容列',
              ruleId: 'FSSJY00049',
              errorInfo: '费率表信息缺失，未找到“措施项目费”'
            })
          }
          // 判断是否包含分部分项费
          if (!colValue.includes('分部分项费')) {
            this.checkResult.push({
              fileName: this.fileName,
              sheetName: worksheet.name,
              tableType: sheetType,
              checkPlace: '汇总内容列',
              ruleId: 'FSSJY00050',
              errorInfo: '费率表信息缺失，未找到“分部分项费”'
            })
          }
          // 判断是否包含其他项目费
          if (!colValue.includes('其他项目费')) {
            this.checkResult.push({
              fileName: this.fileName,
              sheetName: worksheet.name,
              tableType: sheetType,
              checkPlace: '汇总内容列',
              ruleId: 'FSSJY00051',
              errorInfo: '费率表信息缺失，未找到“其他项目费”'
            })
          }
          // 判断是否包含建设工程造价
          if (!colValue.includes('建设工程造价')) {
            this.checkResult.push({
              fileName: this.fileName,
              sheetName: worksheet.name,
              tableType: sheetType,
              checkPlace: '汇总内容列',
              ruleId: 'FSSJY00052',
              errorInfo: '费率表信息缺失，未找到“建设工程造价”'
            })
          }
        }
      } else {
        // 获取表头检测区单元格对象数组{ address, value }
        const tableTitle = getExcelCellContent(worksheet, 'A3-K5')
        // 检测是否具有序号列
        if (!hasElementInArrayWithValue(['序号', '序'], tableTitle).exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-K5',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00014' : 'FSSJY00023',
            errorInfo: '送审表模板错误，未找到“序号”'
          })
        }
        // 检测是否具有编号列
        if (!endWithStr(tableTitle, '编号').exist && !endWithStr(tableTitle, '编码').exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-K5',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00015' : 'FSSJY00024',
            errorInfo:
              sheetType == '分部分项表'
                ? '送审表模板错误，没有找到送审或结审“编号”'
                : '送审表模板错误，没有找到“编号”'
          })
        }
        // 检测是否具有项目名称列
        if (!endWithStr(tableTitle, '名称').exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-K5',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00016' : 'FSSJY00025',
            errorInfo:
              sheetType == '分部分项表'
                ? '送审表模板错误，没有找到送审或结审“项目名称”'
                : '送审表模板错误，没有找到“项目名称”'
          })
        }
        // 检测是否具有单位列
        if (!endWithStr(tableTitle, '单位').exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-K5',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00017' : 'FSSJY00026',
            errorInfo:
              sheetType == '分部分项表'
                ? '送审表模板错误，没有找到送审或结审“单位”'
                : '送审表模板错误，没有找到“单位”'
          })
        }
        // 检测是否具有数量列
        if (
          !endWithStr(tableTitle, '数量').exist &&
          !hasElementInArrayWithValue(['工程量'], tableTitle).exist
        ) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-K5',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00018' : 'FSSJY00027',
            errorInfo:
              sheetType == '分部分项表'
                ? '送审表模板错误，没有找到送审或结审“数量”'
                : '送审表模板错误，没有找到“数量”'
          })
        }
        // 检测是否具有单价列
        if (!includeStr(tableTitle, '单价').exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-K5',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00019' : 'FSSJY00028',
            errorInfo:
              sheetType == '分部分项表'
                ? '送审表模板错误，没有找到送审或结审“单价”'
                : '送审表模板错误，没有找到“单价”'
          })
        }
        // 检测是否具有合价列
        if (!includeStr(tableTitle, '合价').exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A3-K5',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00020' : 'FSSJY00029',
            errorInfo:
              sheetType == '分部分项表'
                ? '送审表模板错误，没有找到送审或结审“合价”'
                : '送审表模板错误，没有找到“合价”'
          })
        }
        // 获取合计行对象
        const totalRowObj = hasElementInArrayWithValue(
          ['合计', '总计', '汇总'],
          getColsArr(worksheet, 'A-C列')
        )
        // 无合计行报错返回
        if (!totalRowObj.exist) {
          this.checkResult.push({
            fileName: this.fileName,
            sheetName: worksheet.name,
            tableType: sheetType,
            checkPlace: 'A-C列',
            ruleId: sheetType == '分部分项表' ? 'FSSJY00033' : 'FSSJY00030',
            errorInfo: '送审表模板错误，没有找到“合计”行'
          })
          return
        }
      }
      // 将表分类挂载到项目列表
      if (projectList.has(projectName)) {
        const project = projectList.get(projectName)
        if (sheetType in project) projectList.get(projectName)[sheetType].push(worksheet)
        else projectList.get(projectName)[sheetType] = [worksheet]
      } else {
        projectList.set(projectName, {
          [sheetType]: [worksheet]
        })
      }
    })
    return projectList
  }

  getResult() {
    return this.checkResult
  }
}

// 导出类
export default FormatValidation
